#pragma once
#include <QString>
#include <QDateTime>
#include <iostream>
#ifdef __linux__
#include <unistd.h>
#endif

namespace cloud
{

class logger
{
public:
    enum class log_level
    {
        on = -1,
        debug,
        info,
        warn,
        error,
        fatal,
    };

    static void debug(const char* file, int line, const char* func, const QString& payload) { log(file, line, func, log_level::debug, payload); }
    static void  info(const char* file, int line, const char* func, const QString& payload) { log(file, line, func, log_level:: info, payload); }
    static void  warn(const char* file, int line, const char* func, const QString& payload) { log(file, line, func, log_level:: warn, payload); }
    static void error(const char* file, int line, const char* func, const QString& payload) { log(file, line, func, log_level::error, payload); }
    static void fatal(const char* file, int line, const char* func, const QString& payload) { log(file, line, func, log_level::fatal, payload); }

    static void setBaseLevel(log_level level) { _level = level; }

    static log_level str2Level(QString str)
    {
        str = str.toLower();
        if (str == "debug") return log_level::debug;
        if (str == "info" ) return log_level::info;
        if (str == "warn" ) return log_level::warn;
        if (str == "error") return log_level::error;
        if (str == "fatal") return log_level::fatal;
        return log_level::on;
    }

private:
    static void log(const char* file, int line, const char* func, log_level level, const QString& payload)
    {
        if (level < _level) return;

        QString date = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
        QString msg = QString("[%1] [%2] [%3:%4]\t%5\t[%6]")
                                .arg(date)
                                .arg(getLevel(level))
                                .arg(file)
                                .arg(line)
                                .arg(payload)
                                .arg(func); // __PRETTY_FUNCTION__

        std::cout << msg.toUtf8().constData() << std::endl;
        fflush(stdout);
#ifdef __linux__
        fsync(1);
#endif
    }

    static QString getLevel(log_level type)
    {
        if (type == log_level::debug) return "DEBUG";
        if (type == log_level::info ) return "INFO";
        if (type == log_level::warn ) return "WARN";
        if (type == log_level::error) return "ERROR";
        if (type == log_level::fatal) return "FATAL";
        return "ON";
    }

private:
    static log_level _level;
};

#define DEBUG(s) cloud::logger::debug(__FILE__, __LINE__, __FUNCTION__, s)
#define  INFO(s) cloud::logger:: info(__FILE__, __LINE__, __FUNCTION__, s)
#define  WARN(s) cloud::logger:: warn(__FILE__, __LINE__, __FUNCTION__, s)
#define ERROR(s) cloud::logger::error(__FILE__, __LINE__, __FUNCTION__, s)
#define FATAL(s) cloud::logger::fatal(__FILE__, __LINE__, __FUNCTION__, s)

}
